#include <stdbool.h>

#include "mcl/val.h"
#include "mcl/da.h"
#include "mcl/rt.h"
#include "mcl/log.h"
#include "mcl/writer.h"
#include "mcl/config.h"

struct vtype_info VTYPE_INFO = {
	.name =
		{
#define _(NAME, HUMAN_NAME) [MC_##NAME] = #HUMAN_NAME,
			VTYPE_MAP(_)
#undef _
		},
};

struct lvalue *new_val(struct runtime *rt, enum vtype type)
{
	struct lvalue *v = (struct lvalue *)malloc(sizeof(struct lvalue));
	v->type = type;
	DA_APPEND(rt->living[rt->currl], v);
	return v;
}

struct lvalue *
new_builtin(struct env *env, fn_builtin builtin, struct da_dstr parameters)
{
	struct lvalue *v = (struct lvalue *)malloc(sizeof(struct lvalue));

	v->type = MC_FUNC;
	v->via.func = new_lambda_builtin(env, parameters, builtin);
	return v;
}

static char const *INDENT = "  ";

static void write_indent(struct writer *w, usize indent)
{
	for (usize i = 0; i < indent; ++i) {
		write_cstr(w, INDENT);
	}
}

// static const bool WRITE_VAL_PTR = false;

void write_list(struct writer *w, usize indent, struct slice *vl)
{
	write_indent(w, indent);
	write_cstr(w, "(");

	for (usize i = 0; i < vl->len; ++i) {
		if (i != 0) {
			write_cstr(w, " ");
		}

		write_val(w, 0, &vl->data[i]);
	}

	write_cstr(w, ")");
}

void write_val(struct writer *w, usize indent, struct lvalue *v)
{
	switch (v->type) {
	case MC_NIL:
		write_indent(w, indent);
		write_cstr(w, "nil");
		break;
	case MC_BOOL:
		write_indent(w, indent);
		write_cstr(w, v->via.bool_ ? "true" : "false");
		break;
	case MC_I64:
		write_indent(w, indent);
		write_i64(w, v->via.i64);
		break;
	case MC_STR:
		write_indent(w, indent);
		write_cstr(w, "`");
		write_dstr(w, &v->via.str);
		write_cstr(w, "'");
		break;
	case MC_SYM:
		write_indent(w, indent);
		write_dstr(w, &v->via.sym);
		break;
	case MC_LIST:
		write_list(w, indent, &v->via.list);
		break;
	case MC_FUNC:
		write_indent(w, indent);
		write_cstr(w, "<func>");
		break;
	case MC_MACRO:
		write_indent(w, indent);
		write_cstr(w, "<macro>");
		break;
	case MC_ERROR:
		write_indent(w, indent);
		write_cstr(w, "<error: `");
		write_dstr(w, &v->via.err.message);
		write_cstr(w, "'>");
		break;
	case MC_LAZY:
		if (v->via.lazy->v == NULL) {
			write_indent(w, indent);
			write_cstr(w, "<lazy>");
		} else {
			write_val(w, indent, v->via.lazy->v);
		}
		break;
	case MC_PARAMETER:
		write_indent(w, indent);
		write_cstr(w, "<parameter: ");
		write_i64(w, v->via.parameter);
		write_cstr(w, ">");
		break;
	}
}

void val_dbg(char const *name, struct lvalue *v)
{
	struct writer w = new_writer_from_buffer();
	write_val(&w, 2, v);

	logdebug("%s =\n%.*s", name, (int)w.via.buf.len, w.via.buf.data);

	writer_deinit(&w);
}

void vl_dbg(char const *name, struct slice *vl)
{
	struct writer w = new_writer_from_buffer();
	write_list(&w, 2, vl);

	loginfo("%s =\n%.*s", name, (int)w.via.buf.len, w.via.buf.data);

	writer_deinit(&w);
}

#if IS_ON(CONFIG_TEST)
void test_list_reverse(void)
{
}
#endif
